#include "ssd1306.h"
#include "cw32l010_systick.h"
#include "font.h"
#include "icon.h"
#include "cw32l010_rtc.h"
#include "stdio.h"
#include "rda5807m.h"
oled_info_t oled_info;
uint8_t OLED_GRAM[128][8];

void OLED_GPIO_INTI(void)
{
    SYSCTRL_AHBPeriphClk_Enable(SPI_SCK_GPIO_CLK | SPI_MOSI_GPIO_CLK | SPI_CS_GPIO_CLK, ENABLE);
    SYSCTRL_APBPeriphClk_Enable1(SYSCTRL_APB1_PERIPH_SPI, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure = {0};
    // SCK PA5
    GPIO_InitStructure.Pins = SPI_SCK_GPIO_PIN;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_Init(SPI_SCK_GPIO_PORT, &GPIO_InitStructure);
    // MOSI PA6
    GPIO_InitStructure.Pins = SPI_MOSI_GPIO_PIN;
    // GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_Init(SPI_MOSI_GPIO_PORT, &GPIO_InitStructure);
    // CS引脚 PA2
    GPIO_InitStructure.Pins = SPI_CS_GPIO_PIN;
    GPIO_Init(SPI_CS_GPIO_PORT, &GPIO_InitStructure);
    // 拉高CS
    SPI_CS_HIGH();
    SPI_SCK_AF();
    SPI_MOSI_AF();

    // rest(PB00) 拉低复位，拉高正常
    GPIO_InitStructure.Pins = GPIO_PIN_0;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_Init(CW_GPIOB, &GPIO_InitStructure);
    PB00_SETHIGH();
    // DC(pa04)
    GPIO_InitStructure.Pins = GPIO_PIN_4;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_Init(CW_GPIOA, &GPIO_InitStructure);
    PA04_SETHIGH();
}
void OLED_SPI_INIT(void)
{
    SPI_InitTypeDef SPI_InitStructure = {0};
    SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_TxOnly;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                      // 主机模式
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                  // 帧数据长度为8bit
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;                        // 时钟空闲电平为高
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;                       // 第二个边沿采样
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                          // 片选信号由SSI寄存器控制
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; // 波特率为PCLK的2分频
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;                 // 最高有效位 MSB 收发在前
    SPI_InitStructure.SPI_Speed = SPI_Speed_High;                      // 高速SPI
    SPI_Init(CW_SPI, &SPI_InitStructure);
    SPI_Cmd(CW_SPI, ENABLE);
}
// 写命令
void OLED_WRITE_CMD(uint8_t cmd)
{
    SPI_CS_LOW();
    DC_WRITE_CMD();
    while (SPI_GetFlagStatus(CW_SPI, SPI_FLAG_TXE) == RESET)
        ;
    SPI_SendData(CW_SPI, cmd);
    while (SPI_GetFlagStatus(CW_SPI, SPI_FLAG_BUSY) == SET)
        ; // 等待本次发送完成
    SPI_CS_HIGH();
}
// 写数据
void OLED_WRITE_DATA(uint8_t data)
{
    SPI_CS_LOW();
    DC_WRITE_DATA();
    while (SPI_GetFlagStatus(CW_SPI, SPI_FLAG_TXE) == RESET)
        ;
    SPI_SendData(CW_SPI, data);
    while (SPI_GetFlagStatus(CW_SPI, SPI_FLAG_BUSY) == SET)
        ; // 等待本次发送完成
    SPI_CS_HIGH();
}

/* 图像刷新函数 */
void OLED_Refresh_GRAM(void)
{
    uint8_t i, j;
    for (i = 0; i < 8; i++)
    {
        /* 设置显示的起始地址 */
        OLED_WRITE_CMD(0xB0 + i); // 设置页地址（行）
        OLED_WRITE_CMD(0x00);     // 设置列地址的低四位
        OLED_WRITE_CMD(0x10);     // 设置列地址的高四位
        for (j = 0; j < 128; j++)
        {
            OLED_WRITE_DATA(OLED_GRAM[j][i]); // 将GRAM中图像信息写入屏幕
        }
    }
}
/* 清屏函数 */
void OLED_Clear(void)
{
    // uint8_t i, j;
    // for (i = 0; i < 8; i++)
    //     for (j = 0; j < 128; j++)
    //         OLED_GRAM[j][i] = 0x00;
    //OLED_Refresh_GRAM();
    memset(OLED_GRAM, 0, sizeof(OLED_GRAM));
}
void OLED_ClearArea(uint8_t x_start, uint8_t y_start, uint8_t x_end, uint8_t y_end)
{
    // 参数边界检查
    if(x_start > 127) x_start = 127;
    if(y_start > 63) y_start = 63;
    if(x_end > 127) x_end = 127;
    if(y_end > 63) y_end = 63;
    
    // 确保起始坐标不大于结束坐标
    if(x_start > x_end)
    {
        uint8_t temp = x_start;
        x_start = x_end;
        x_end = temp;
    }
    
    if(y_start > y_end)
    {
        uint8_t temp = y_start;
        x_start = y_end;
        y_end = temp;
    }
    
    // 计算影响的页范围 (每页8行)
    uint8_t start_page = y_start / 8;
    uint8_t end_page = y_end / 8;
    
    // 计算每页中的位掩码
    uint8_t start_mask = 0xFF << (y_start % 8);
    uint8_t end_mask = 0xFF >> (7 - (y_end % 8));
    
    // 处理单页情况
    if(start_page == end_page)
    {
        uint8_t mask = start_mask & end_mask;
        for(uint8_t x = x_start; x <= x_end; x++)
        {
            OLED_GRAM[x][start_page] &= ~mask;
        }
    }
    else
    {
        // 处理第一页
        for(uint8_t x = x_start; x <= x_end; x++)
        {
            OLED_GRAM[x][start_page] &= ~start_mask;
        }
        
        // 处理中间完整页
        for(uint8_t page = start_page + 1; page < end_page; page++)
        {
            for(uint8_t x = x_start; x <= x_end; x++)
            {
                OLED_GRAM[x][page] = 0x00;
            }
        }
        
        // 处理最后一页
        for(uint8_t x = x_start; x <= x_end; x++)
        {
            OLED_GRAM[x][end_page] &= ~end_mask;
        }
    }
}
void OLED_FULL(void)
{
    uint8_t i, j;
    for (i = 0; i < 8; i++)
        for (j = 0; j < 128; j++)
            OLED_GRAM[j][i] = 0xFF;
    OLED_Refresh_GRAM();
}
void OLED_Inti(void)
{
    // 执行复位操作
    PB00_SETLOW(); // 拉低REST
    SysTickDelay(2);
    PB00_SETHIGH(); // 拉高REST
    OLED_WRITE_CMD(0xAE); // 关闭显示
    OLED_WRITE_CMD(0xD5); // 设置时钟分频因子
    OLED_WRITE_CMD(80);
    OLED_WRITE_CMD(0xA8); // 设置驱动路数
    OLED_WRITE_CMD(0x3F); // 路数默认0x3F（1/64）
    OLED_WRITE_CMD(0xD3); // 设置显示偏移
    OLED_WRITE_CMD(0x00); // 偏移默认为0
    OLED_WRITE_CMD(0x40); // 设置显示开始行[5:0]
    OLED_WRITE_CMD(0x8D); // 电荷泵设置
    OLED_WRITE_CMD(0x14); // bit2，开启/关闭
    OLED_WRITE_CMD(0x20); // 设置内存地址模式
    OLED_WRITE_CMD(0x02); //[1:0],00，列地址模式;01，行地址模式;10,页地址模式;默认10;
    OLED_WRITE_CMD(0xA1); // 段重定义设置,bit0:0,0->0;1,0->127;
    OLED_WRITE_CMD(0xC0); // 设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数
    OLED_WRITE_CMD(0xDA); // 设置COM硬件引脚配置
    OLED_WRITE_CMD(0x12); //[5:4]配置
    OLED_WRITE_CMD(0x81); // 对比度设置
    OLED_WRITE_CMD(0xFF); // 默认0x7F（范围1~255，越大越亮）
    OLED_WRITE_CMD(0xD9); // 设置预充电周期
    OLED_WRITE_CMD(0xF1); //[3:0],PHASE 1;[7:4],PHASE 2;
    OLED_WRITE_CMD(0xDB); // 设置VCOMH 电压倍率
    OLED_WRITE_CMD(0x30); //[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc;
    OLED_WRITE_CMD(0xA4); // 全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏)
    OLED_WRITE_CMD(0xA6); // 设置显示方式;bit0:1,反相显示;0,正常显示
    OLED_WRITE_CMD(0xAF); // 开启显示
    OLED_Clear();
    OLED_CTRL_BL(0x01);
}
// OLED_GRAM[128][8]
/* 画点函数，以屏幕像素点为单位，以左上角为原点 x:0~127 y:0~63
（x,y）坐标换算:OLED_GRAM[x][7-y/8]|=1<<(7-y%8)；

mode取1正显，取0反显
*/
void OLED_DrawPoint(uint8_t x, uint8_t y, uint8_t mode)
{
    /* 主要是求出y坐标，根据y坐标来位运算OLED_GRAM中相应的位 */
    uint8_t i, j, temp;
    /* 判断坐标是否超出范围 */
    if (x > 127 || y > 63)
        return;
    i = 7 - y / 8; // 算出第几页
    j = y % 8;
    temp = 0x01 << (7 - j); // 由位运算精确找出坐标像素点
    if (mode == 0)
        OLED_GRAM[x][i] &= ~temp;
    else
        OLED_GRAM[x][i] |= temp;
}

/* 在（x,y）坐标正显/反显指定大小字符chr
mode：0是反显，1是正常显示
size：12/16/24
ASCII字符集: !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
*/
void OLED_Show_Char(uint8_t x, uint8_t y, uint8_t chr, uint8_t size, uint8_t mode)
{
    /* temp是当前对应的一个字节的点集，y0是初始坐标 */
    uint8_t temp, t, t1;
    uint8_t y0 = y;
    /* csize是单个字符所占字节的多少 */
    uint8_t csize = (size / 8 + ((size % 8) ? 1 : 0)) * (size / 2);
    /* 求出偏移的地址差，即得到该字符在字库中的序号（从零开始） */
    /* chr会锁定字符在字库中的的序号 */
    chr = chr - ' ';

    /* 相应字符点集有多少个字节 */
    for (t = 0; t < csize; t++)
    {
        /* 根据字符的大小选择相应字库，根据chr得到具体的字符地址 */
        switch (size)
        {
        case 12:
            temp = asc2_1206[chr][t];
            break; // 12x6(行x列)
        case 16:
            temp = font_16x8[chr][t];
            break; // 16x8
        // case 24:temp=asc2_2412[chr][t];break;//24x12
        default:
            return; // 没有相应字库
        }

        /* 画出每一列的点 */
        for (t1 = 0; t1 < 8; t1++)
        {
            if (temp & 0x80)
                OLED_DrawPoint(x, y, mode);
            else
                OLED_DrawPoint(x, y, !mode);

            temp <<= 1;
            y++;

            /* 根据给出每一列的像素点的多少，判断列是否满：
            满了就换列，不满继续在此列画点 （与字库画点原理相关）
            */
            if ((y - y0) == size)
            {
                y = y0;
                x++;
                /* 一列满，跳出循环，直接转到下一个字节点集的打印 */
                break;
            }
        }
    }
}
/* （x,y）是显示的坐标，*p是字符串的首地址，size是字符点集大小 */
void OLED_Show_String(uint8_t x, uint8_t y, const uint8_t *p, uint8_t size, uint8_t mode)
{
    /* 判断是否合法字符，同时也限定了范围 */
    while ((*p <= '~') && (*p >= ' '))
    {
        /* 如果初始行放不下，移动到下一行 */
        if (x > (128 - (size / 2)))
        {
            x = 0;
            y = y + size;
        }
        if (y > (64 - size))
        {
            x = y = 0;
            OLED_Clear();
        }

        OLED_Show_Char(x, y, *p, size, mode);
        /* 移动到下一个字符位置，size/2是因为做点集时就是：行X列，而且 行=2X列，所以size就是行数 */
        x = x + size / 2;
        p++;
    }
}

void OLED_ShowIcon(uint8_t x, uint8_t y, const uint8_t *icon, uint8_t width, uint8_t height, uint8_t mode)
{
    uint8_t temp;
    uint16_t i, j;

    // 计算图标数据在字节数组中的位置
    for (j = 0; j < height; j++)
    {
        for (i = 0; i < width; i++)
        {
            // 获取当前像素点的值
            if (icon[(j * width + i) / 8] & (0x80 >> ((j * width + i) % 8)))
            {
                OLED_DrawPoint(x + i, y + j, mode);
            }
            else
            {
                OLED_DrawPoint(x + i, y + j, !mode);
            }
        }
    }
}

void OLED_ShowChar_16x8(uint8_t x, uint8_t y, uint8_t chr, uint8_t mode)
{
    uint8_t i, j, temp;

    // 确保字符在可显示范围内 (空格到波浪线)
    if (chr < ' ' || chr > '~')
        return;

    // 计算字符在字体数组中的索引
    uint8_t char_index = chr - ' ';

    // 确保坐标在有效范围内
    if (x > 127 || y > 63)
        return;

    // 逐行绘制字符
    for (i = 0; i < 16; i++)
    {                                    // 16行高度
        temp = font_16x8[char_index][i]; // 获取当前行的像素数据

        // 逐列绘制当前行的像素
        for (j = 0; j < 8; j++)
        { // 8列宽度
            if (temp & (0x80 >> j))
            { // 检查每一位是否置位
                OLED_DrawPoint(x + j, y + i, mode);
            }
            else
            {
                OLED_DrawPoint(x + j, y + i, !mode);
            }
        }
    }
}

void OLED_Show_String_16x8(uint8_t x, uint8_t y, const char *str, uint8_t mode)
{
    while (*str)
    {
        OLED_ShowChar_16x8(x, y, *str, mode);
        x += 8; // 每个字符宽度为8像素

        // 如果超出屏幕宽度，换行
        if (x > 120)
        {
            x = 0;
            y += 16; // 每个字符高度为16像素
        }
        str++;
    }
}
// 设置背光,mode 0 减少 1，增加
void OLED_CTRL_BL(uint8_t mode)
{
    OLED_WRITE_CMD(0x81); // 对比度设置
    if(mode){
        if(oled_info.bright_levl<=254) oled_info.bright_levl++;
        OLED_WRITE_CMD(oled_info.bright_levl); // 默认0x7F（范围1~255，越大越亮）
    }
    else {
        if(oled_info.bright_levl>=2) oled_info.bright_levl--;
        OLED_WRITE_CMD(oled_info.bright_levl); // 默认0x7F（范围1~255，越大越亮）
    }   
}
void OLED_TIME(void)
{
    RTC_TimeTypeDef RTC_TimeStruct = {0};
    RTC_DateTypeDef RTC_DateStruct = {0};
    RTC_GetDate(&RTC_DateStruct);
    RTC_GetTime(&RTC_TimeStruct);
    uint8_t date[12], time[9];
    sprintf(date, "20%02x/%02x/%02x", RTC_DateStruct.Year, RTC_DateStruct.Month, RTC_DateStruct.Day);
    sprintf(time, "%02x:%02x:%02x", RTC_TimeStruct.Hour, RTC_TimeStruct.Minute, RTC_TimeStruct.Second);
    OLED_Show_String(0,0, date, 12, 1);
    OLED_Show_String(72,0, time, 12, 1);
}
//显示FM信息
void OLED_FM(void){
    FM_GET_INFO();
    uint8_t fm[9],id[5],rssi[4],vol[3],battery[6];
    sprintf(fm,"%.1fMhz",fm_info.freq);
    sprintf(rssi,"%d",fm_info.rssi);
    sprintf(id,"%x",fm_info.chip_id);
    sprintf(vol,"%02d",fm_info.vol);
    sprintf(battery,"%.1fV",fm_info.battery);
    OLED_Show_String(0, 12,"CH:", 12, 0);
    OLED_Show_String(22, 12, fm, 12, 1);
    OLED_Show_String(72, 12,"RSSI:", 12, 0);
    OLED_Show_String(108, 12, rssi, 12, 1);
    OLED_Show_String(0, 24, "ID:", 12, 0);
    OLED_Show_String(22, 24, id, 12, 1);
    OLED_Show_String(72, 24, "VOL: ", 12, 0);
    OLED_Show_String(108, 24, vol, 12, 1);
    // OLED_Show_String(78, 2, battery, 12, 1);
}
void OLED_DEMO(void)
{
    // 显示字符串
    // OLED_ShowIcon(0,0,top_icon_16x128,128,16,0);
    // OLED_Show_String(0, 2, "RDA5807M V00", 12, 1);
    OLED_TIME();
    OLED_FM();
    OLED_Refresh_GRAM();
    OLED_Clear();
    SysTickDelay(10);
}